package com.wechat.page.chat.content.chatting;

import java.io.File;
import java.net.URI;
import java.util.List;

import org.controlsfx.glyphfont.FontAwesome;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.html.HTMLElement;

import com.swak.OS;
import com.swak.fx.support.AbstractComp;
import com.swak.fx.support.AbstractPage;
import com.swak.fx.support.FXMLView;
import com.swak.fx.support.snapshot.FileSnapshot;
import com.swak.fx.support.ui.UI;
import com.swak.fx.support.ui.UIButton;
import com.swak.fx.support.ui.UIEditor;
import com.swak.fx.support.ui.UIEditor.Event;
import com.swak.fx.support.ui.UIEditor.Keys;
import com.swak.fx.support.ui.UIIcon;
import com.swak.ui.Events;
import com.swak.utils.Lists;
import com.swak.utils.StringUtils;
import com.wechat.Cons;
import com.wechat.dto.ChatDTO;
import com.wechat.dto.ChatInputDTO;
import com.wechat.face.Face;
import com.wechat.face.Faces;
import com.wechat.keymap.Keymaps;
import com.wechat.page.Medias;

import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.control.Button;
import javafx.scene.input.Clipboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;

/**
 * ChattingInputComp.
 * 
 * @author 618lf
 */
@FXMLView(title = "聊天发送框", value = "ChattingInputComp.fxml", css = "ChattingInputComp.css")
public class ChattingInputComp extends AbstractComp {

	@FXML
	private GridPane content;
	@FXML
	private HBox top;
	@FXML
	private StackPane bottom;
	@FXML
	private UIEditor editor;
	private Button sendButton;

	private UIIcon emoBtn;
	private UIIcon fileBtn;
	private UIIcon cutBtn;
	private UIIcon historyBtn;
	private UIIcon phoneBtn;
	private UIIcon videoBtn;

	private FacesSelectComp facesSelect;

	public ChattingInputComp(AbstractPage parent) {
		super(parent);
		this.sendButton = new UIButton("发送(S)");
		this.bottom.getChildren().add(this.sendButton);
		this.bottom.setAlignment(Pos.CENTER_RIGHT);

		this.emoBtn = new UIIcon().Icon(FontAwesome.Glyph.SMILE_ALT);
		this.fileBtn = new UIIcon().Icon(FontAwesome.Glyph.FOLDER_ALT);
		this.cutBtn = new UIIcon().Icon(FontAwesome.Glyph.CUT);
		this.historyBtn = new UIIcon().Icon(FontAwesome.Glyph.COMMENT_ALT);
		this.phoneBtn = new UIIcon().Icon(FontAwesome.Glyph.PHONE);
		this.videoBtn = new UIIcon().Icon(FontAwesome.Glyph.CAMERA);

		Pane placeholder = new Pane();
		HBox.setHgrow(placeholder, Priority.ALWAYS);
		this.top.getChildren().addAll(emoBtn, fileBtn, cutBtn, historyBtn, placeholder, phoneBtn, videoBtn);
	}

	@Override
	public void initializeParts() {
		super.initializeParts();
		this.initBtn(emoBtn);
		this.initBtn(fileBtn);
		this.initBtn(cutBtn);
		this.initBtn(historyBtn);
		this.initBtn(phoneBtn);
		this.initBtn(videoBtn);
		this.top.setAlignment(Pos.CENTER);
		this.content.setViewOrder(0);
	}

	private void initBtn(UIIcon btn) {
		btn.Size(20);
		btn.setPrefHeight(34);
		btn.setPrefWidth(34);
		btn.setAlignment(Pos.CENTER);
		btn.setCursor(Cursor.HAND);
	}

	@Override
	public void setupEventHandlers() {
		super.setupEventHandlers();
		this.sendButton.setOnAction((e) -> this.doSend());
		this.emoBtn.addEventHandler(MouseEvent.MOUSE_CLICKED, (e) -> {
			if (this.facesSelect == null) {
				this.initFacesSelectComp();
			}
			this.facesSelect.show(this.emoBtn);
		});
	}

	private void initFacesSelectComp() {
		this.facesSelect = FacesSelectComp.New(FacesSelectComp.class);
		this.facesSelect.$on("face:select", (p) -> this.insertFace((Face) p));
	}

	@Override
	public void setupValueChangedListeners() {
		super.setupValueChangedListeners();
		this.root.focusedProperty().addListener((observable, oldValue, newValue) -> {
			Platform.runLater(() -> {
				if (newValue) {
					this.editor.requestFocus();
				}
			});
		});
		this.editor.$on(Event.Copy, e -> this.doCopy());
		this.editor.$on(Event.Drag_Copy, e -> this.doDragCopy((Clipboard) e));
		this.editor.$on(Event.Key_Typed, e -> this.executeKeysCommand((Keys) e));
		this.editor.$on(Event.Dblclick, e -> this.onPreview((HTMLElement) e));
		this.chatProperty().addListener(this::onChatActive);
	}

	private void onChatActive(ObservableValue<? extends ChatDTO> ob, ChatDTO unActive, ChatDTO active) {

		// 序列化到草稿
		if (unActive != null) {
			ChatInputDTO draft = this.parseDraft();
			unActive.setDraft(draft);

			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("[聊天框 - 取消激活] - 发送事件 ：{}", Cons.Event_Chat_Refresh);
			}

			// 发送一个事件
			Events.post(new com.swak.ui.Event(Cons.Event_Chat_Refresh, unActive));
		}

		// 显示当前的草稿
		if (active != null && active.getDraft() != null) {
			this.editor.setBodyHtml(active.getDraft().getInput());
		}

		// 当前的也需要刷新
		if (active != null) {

			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("[聊天框 - 激活聊天] - 发送事件 ：{}", Cons.Event_Chat_Refresh);
			}

			// 发送一个事件
			Events.post(new com.swak.ui.Event(Cons.Event_Chat_Refresh, active));
		}
	}

	@Override
	public void initializeFinish() {
		super.initializeFinish();
	}

	private void executeKeysCommand(Keys keys) {
		com.wechat.enums.Events command = Keymaps.findKeysCommand(keys);
		if (command == null) {
			return;
		}
		if (command == com.wechat.enums.Events.Event_Newline) {
			this.editor.newLine();
		} else if (command == com.wechat.enums.Events.Event_Send) {
			this.doSend();
		}
	}

	private void doCopy() {
		this.doDragCopy(Clipboard.getSystemClipboard());
	}

	private void doDragCopy(Clipboard clipboard) {
		List<File> files = clipboard.getFiles();
		String text = clipboard.getString();
		String html = clipboard.getHtml();
		Object object = clipboard.getContent(UI.ClipboardDataFormat.OBJECT);
		this.doDragCopyForFiles(files);
		this.doDragCopyForText(text);
		this.doDragCopyForHtml(html);
		this.doDragCopyForObject(object);
	}

	private void doDragCopyForFiles(List<File> files) {
		if (files != null && !files.isEmpty()) {
			for (File file : files) {
				Medias media = Medias.of(file.getName());
				if (media == Medias.Image) {
					this.doDragCopyForImage(file);
				} else {
					this.doDragCopyForFile(file);
				}
			}
		}
	}

	private void doDragCopyForImage(File file) {
		this.insertImage(file.toURI().toString());
	}

	private void doDragCopyForFile(File file) {
		FileSnapshot snapshot = FileSnapshot.of(file);
		this.addChild(snapshot.getRoot());
		File image = snapshot.output();
		this.removeChild(snapshot.getRoot());
		this.insertImage(image.toURI().toString(), file.toURI().toString());
	}

	private void doDragCopyForText(String text) {

	}

	private void doDragCopyForHtml(String html) {

	}

	private void doDragCopyForObject(Object object) {

	}

	private void onPreview(HTMLElement element) {
		String file = element.getAttribute("data-file");
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("[打开文件]: {}", file);
		}
		if (StringUtils.isNotBlank(file)) {
			OS.open(file);
		}
	}

	// ================================================================================
	// Append Message
	// ================================================================================

	private void insertFace(Face face) {
		String html = StringUtils.format("<img src=\"%s\" data-face=\"%s\" >", face.getRealPath(), face.Encode());
		this.editor.insertHtml(html);
	}

	private void insertImage(String url) {
		String html = StringUtils.format("<img src=\"%s\" data-file=\"%s\" style=\"%s\">", url, url,
				"max-width:150px;");
		this.editor.insertHtml(html);
	}

	private void insertImage(String url, String file) {
		String html = StringUtils.format("<img src=\"%s\" draggable=\"false\" data-file=\"%s\" style=\"%s\">", url,
				file, "max-width:250px;");
		this.editor.insertHtml(html);
	}

	// ================================================================================
	// Parse and send message
	// ================================================================================

	private void doSend() {
		List<Object> results = Lists.newArrayList();
		HTMLElement body = this.editor.getHTMLBody();
		this.parseNodes(body, results);
		this.editor.clearBodyHtml();
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("[发送数据]：{}", results);
		}
		this.$emit(com.wechat.enums.Events.Event_Send.name(), results);
	}

	// 创建草稿的简述
	final ChatInputDTO parseDraft() {
		ChatInputDTO draft = new ChatInputDTO();
		List<Object> results = Lists.newArrayList();
		HTMLElement body = this.editor.getHTMLBody();

		StringBuilder brief = new StringBuilder();
		this.parseNodes(body, results);
		for (Object msg : results) {
			String text = StringUtils.EMPTY;
			if (msg instanceof String) {
				text = Faces.parseBriefString((String) msg);
			} else if (msg instanceof File) {
				Medias media = Medias.of(((File) msg).getName());
				text = media.getMess().getDescribe();
			}

			if (brief.length() + text.length() <= 255) {
				brief.append(text);
			}
			if (brief.length() >= 255) {
				break;
			}
		}
		String input = this.editor.getBodyHTML();
		this.editor.clearBodyHtml();
		return draft.setInput(input).setBrief(brief.toString());
	}

	final void parseNodes(Node node, List<Object> results) {
		if (node.getNodeType() == Node.ELEMENT_NODE && "IMG".equals(node.getNodeName())) {
			this.parseImgNode(node, results);
		} else if (node.getNodeType() == Node.ELEMENT_NODE && "BODY".equals(node.getNodeName())) {
			this.parseDivNodes(node, results);
		} else if (node.getNodeType() == Node.ELEMENT_NODE && "DIV".equals(node.getNodeName())) {
			this.parseDivNodes(node, results);
		} else {
			results.add(node.getTextContent());
		}
	}

	final void parseDivNodes(Node node, List<Object> results) {
		NodeList children = node.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			this.parseNodes(children.item(i), results);
		}
	}

	final void parseImgNode(Node node, List<Object> results) {
		Node file = node.getAttributes().getNamedItem("data-file");
		Node face = node.getAttributes().getNamedItem("data-face");
		if (face != null && StringUtils.isNotBlank(face.getNodeValue())) {
			results.add(face.getNodeValue());
		} else if (file != null && StringUtils.isNotBlank(file.getNodeValue())) {
			results.add(new File(URI.create(file.getNodeValue())));
		}
	}

	// ================================================================================
	// Propertys
	// ================================================================================

	private final ObjectProperty<ChatDTO> chatProperty = new SimpleObjectProperty<>();

	public ObjectProperty<ChatDTO> chatProperty() {
		return chatProperty;
	}
}